package com.xuyuan.concurrent.atomic;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;

public class AtomicExample {

    /**
     * AtomicInteger：直接操作一个整数值
     * AtomicIntegerFieldUpdater：操作的是某个对象中的 int 字段, 需要指定字段的 volatile 修饰符。
     */
    @Test
    public void test1() {
        AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "score");
        User user = new User();
        updater.set(user, 100);
        updater.getAndAdd(user, 100);
        System.out.println(user.getScore());
    }

    /**
     * AtomicReference: 操作引用类型
     * AtomicStampedReference: 操作带有版本号的引用类型,用于解决 ABA 问题
     * AtomicMarkableReference: 不关心版本号，只关心是否被修改过
     */
    @Test
    public void test2() {
        User user1 = new User("xuyuan1");
        User user2 = new User("xuyuan2");

        AtomicReference<User> userAtomicReference = new AtomicReference<>(user1);
        userAtomicReference.compareAndSet(user1, user2);
        System.out.println(userAtomicReference.get().getName());

        AtomicStampedReference atomicStampedReference = new AtomicStampedReference(user1, 1);
        atomicStampedReference.compareAndSet(user1, user2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        atomicStampedReference.compareAndSet(user2, user1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        User reference = (User) atomicStampedReference.getReference();
        System.out.println(reference);
    }

    private LongAdder longAdder = new LongAdder();

    private Integer[] arrayOne = new Integer[]{0, 1, 2, 3, 0, 5, 6, 0, 56, 0};

    private Integer[] arrayTwo = new Integer[]{10, 1, 2, 3, 0, 5, 6, 0, 56, 0};

    /**
     * AtomicLong
     * LongAdder：使用AtomicLong时，在高并发下大量线程会同时去竞争更新同一个原子变量，但是由于同时只有一个线程的CAS操作会成功，这就造成了大量线程竞争失败后，会通过无限循环不断进行自旋尝试CAS的操作，而这会白白浪费CPU资源。
     * 因此JDK 8新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点
     */
    @Test
    public void test3() throws InterruptedException {
        Thread threadOne = new Thread(() -> {
            int size = arrayOne.length;
            for (int i = 0; i < size; ++i) {
                if (arrayOne[i].intValue() == 0) {
                    longAdder.increment();
                }
            }

        });
        Thread threadTwo = new Thread(() -> {
            int size = arrayTwo.length;
            for (int i = 0; i < size; ++i) {
                if (arrayTwo[i].intValue() == 0) {
                    longAdder.increment();
                }
            }
        });
        // 启动子线程
        threadOne.start();
        threadTwo.start();
        // 等待线程执行完毕
        threadOne.join();
        threadTwo.join();
        System.out.println("count 0:" + longAdder.sum());
    }

    /**
     * LongAccumulator: LongAccumulator 和 LongAdder的区别是，LongAccumulator可以接受一个函数作为参数，这个函数用来对原子变量进行操作，LongAdder则没有这个功能。
     */
    @Test
    public void test4() throws InterruptedException {
        LongAccumulator accumulator = new LongAccumulator((x, y) -> x * y, 1L);
        ExecutorService executor = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 4; i++) {
            final int value = i + 1;
            executor.submit(() -> accumulator.accumulate(value));
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.SECONDS);
        long result = accumulator.get();
        System.out.println("累积结果: " + result);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class User {
        volatile int score;
        String id;
        String name;

        public User(String name) {
            this.name = name;
        }
    }
}
